package middleware

import (
	"bytes"
	"context"
	"crypto/sha1"
	"encoding/hex"
	"fmt"

	"github.com/labstack/echo/v4"
	"github.com/patrickmn/go-cache"
	"ldaccount/internal/conf"
	"net/http"
	"runtime/debug"
	"time"

	"ldaccount/pkg/lueerror"
	// "ldaccount/pkg/mail"
	"gitee.com/quantum-force/of-utils/pkg/mail"
)

type errHttp struct {
	cfg *conf.Config
}

var (
	mCache *cache.Cache
	ehPtr  *errHttp
)

func init() {
	mCache = cache.New(10*time.Minute, 20*time.Minute)
	ehPtr = &errHttp{}
}

type httpErrRet struct {
	Code    string      `json:"code"`
	Detail  string      `json:"detail"`
	Msg     string      `json:"msg"`
	Data    interface{} `json:"data"`
	Success bool        `json:"success"`
}

// UnCatchErrorHandler 未知错误处理
func UnCatchErrorHandler(err error, c echo.Context) {
	if err == nil {
		return
	}
	code := http.StatusInternalServerError
	if he, ok := err.(*echo.HTTPError); ok {
		code = he.Code
	}
	errL := lueerror.UnhandledErr(err)
	errRet := &httpErrRet{
		Code:    errL.Code(),
		Detail:  err.Error(),
		Msg:     "小的尽力了！",
		Success: false,
	}
	errT := c.JSON(code, errRet)
	if code > http.StatusInternalServerError {
		sendMail(c.Request().Context(), err, "HTTP")
		if errT != nil {
			logger.For(c.Request().Context()).Error(fmt.Sprintf("异常错误: %s", err.Error()))
		}
	}
}

func sendMail(ctx context.Context, err error, errType string) {
	if err == nil {
		return
	}
	if !isNeedSendMail(ctx, err) {
		return
	}
	rID := ctx.Value("_requestID")
	requestID := ""
	if rID != nil {
		requestID, _ = rID.(string)
	}
	errStack := fmt.Errorf("  detail: %s\n\n  request_id: %s\n\n  trace_log: %s\n", err.Error(), requestID, string(debug.Stack()))
	mailMsg := &mail.Message{
		Subject:   fmt.Sprintf("Bilin-Account %s 异常", errType),
		Content:   bytes.NewBufferString("<pre>" + errStack.Error() + "</pre>"),
		To:        ehPtr.cfg.Base.BugReportTos, // []string{"backend@luedongtech.com"},
		Extension: nil,
	}
	_ = mail.AsyncSend(mailMsg, func(err error) {
		if err != nil {
			logger.For(ctx).Error(fmt.Sprintf("邮件发送错误: %v", err))
		}
	})
}

func isNeedSendMail(_ context.Context, err error) bool {
	if mCache == nil {
		return true
	}
	h := sha1.New()
	_, _ = h.Write([]byte(err.Error()))
	s := hex.EncodeToString(h.Sum(nil))
	key := fmt.Sprintf("bmember:error:%s", s)
	if _, found := mCache.Get(key); found {
		return false
	}
	mCache.Set(key, 1, cache.DefaultExpiration)
	return true
}

func httpLueErrorHandler(err error, c echo.Context) error {
	code := http.StatusInternalServerError
	errRet := &httpErrRet{}
	if lErr, ok := err.(lueerror.LueError); ok {
		ctx := c.Request().Context()
		code = lErr.HttpStatus()
		errRet.Code = lErr.Code()
		errRet.Detail = lErr.Error()
		errRet.Msg = lErr.Alert()
		if lErr.Error() != "context canceled" && lErr.SendMail() {
			sendMail(ctx, lErr, "HTTP")
		}
		err = c.JSON(code, errRet)
		if err != nil {
			UnCatchErrorHandler(err, c)
		}
	} else {
		UnCatchErrorHandler(err, c)
	}
	return nil
}

// EchoErrorHandler 错误拦截中间件
func EchoErrorHandler(next echo.HandlerFunc) echo.HandlerFunc {
	return func(c echo.Context) error {
		err := next(c)
		err = httpLueErrorHandler(err, c)
		return err
	}
}

// EchoErrorHandler 错误拦截中间件工厂
func EchoErrorHandlerFactory(cfg *conf.Config) echo.MiddlewareFunc {
	ehPtr.cfg = cfg
	return EchoErrorHandler
}
